#include <cstdio>
#include <cstdlib>
#include <vector>
#include <pthread.h>
#include <termios.h>
#include <unistd.h>
#include "ourhdr.h"

using std::vector;

Game::Game()
	:stride(1000), m_score(0)
{
	m_screen = new Screen;
	m_snake = new Snake();
	m_food = new Food();
}

Game::~Game(){
	delete m_screen;
	delete m_snake;
	delete m_food;
	delete m_wall;
}

static void del(vector<Pos> &v, const Pos &p){
	for(auto beg = v.begin(); beg != v.end(); beg++){
		if(*beg == p){
			v.erase(beg);
			return;
		}
	}
}

static char input;

void *thread_getchar(void*){
	while(1){
		input = getchar();
		usleep(1000*10);
	}
}

int Game::welcome(){
	char stageStr[] = "stage 01";
	int stage = 1;

	while(1){
		m_screen->clear();
		m_screen->setWords(Pixel::max_row/2-1, "Welcome to Happy Snake!");
		m_screen->setWords(Pixel::max_row/2+1, "Version 1.0. Author: Invoker");
		m_screen->setWords(Pixel::max_row/2+3, stageStr);
		m_screen->setWords(Pixel::max_row/2+4, "Press w or s to select stage");
		m_screen->show();
		char c = getchar();
		switch(c){
			case 'w': 
				stage = (stage + 1) % 100;
				break;
			case 's':
				stage = ((stage - 1) == 0) ? 99 : stage-1;
				break;
			default:
				return stage;
		}
		stageStr[7] = stage % 10 + '0';
		stageStr[6] = stage / 10 + '0';
	}
}

void Game::exec(){
	struct termios old_tio, new_tio;
	tcgetattr(STDIN_FILENO, &old_tio);
	new_tio = old_tio;
	new_tio.c_lflag &= ~ICANON;
	new_tio.c_lflag &= ~ECHO;
	tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);

	int stageNum = welcome();
	m_wall = new Wall(stageNum);

	vector<Pos> free_area;
	for(int i = 0; i < Pos::max_row; i++){
		for(int j = 0; j < Pos::max_col; j++){
			free_area.push_back(Pos(i, j));
		}
	}
	for(int i = 0; i < m_snake->count(); i++){
		del(free_area, (*m_snake)[i].pos());
	}	
	for(int i = 0; i < m_wall->count(); i++){
		del(free_area, (*m_wall)[i].pos());
	}
	m_screen->clear();
	m_snake->castTo(*m_screen);
	m_food->regen(free_area);
	del(free_area, (*m_food)[0].pos());
	m_food->castTo(*m_screen);
	m_wall->castTo(*m_screen);
	m_screen->show();

	pthread_t io_thread;
	if(pthread_create(&io_thread, 0, &thread_getchar, nullptr)){
		printf("fail to creat pthread");
		return;
	}
	char scoreStr[] = "Score:      ";
	while(1){
		Pos nexthead;
		Direction direction = m_snake->direction();
		usleep(stride* 300);
		switch (input){
			case 'q':
				goto finish;
				break;
			case 'w':
				direction = UP;
				break;
			case 'a':
				direction = LEFT;
				break;
			case 's':
				direction = DOWN;
				break;
			case 'd':
				direction = RIGHT;
				break;
			default:
				continue;
		}
		direction = (isOpposite(direction, m_snake->direction())) ? m_snake->direction() : direction;
		m_snake->setDirection(direction);

		/// Determining next head of the snake.
		nexthead = m_snake->head().pos().toDirect(direction);

		if(nexthead == m_food->first().pos()){
			m_snake->eat(nexthead, direction);
			m_score += m_food->value();
			m_food->regen(free_area);
			del(free_area, (*m_food)[0].pos());
		}
		else if(m_snake->hasPos(nexthead) || m_wall->hasPos(nexthead)){
			goto finish;
		}
		else{
			free_area.push_back((*m_snake)[m_snake->count()-1].pos());
			m_snake->move(nexthead, direction);
			del(free_area, nexthead);
		}
		m_screen->clear();
		m_snake->castTo(*m_screen);
		m_food->castTo(*m_screen);
		m_wall->castTo(*m_screen);
		int score = m_score;
		int i = 0;
		if(score >= 100000) score = 99999;
		while(score){
			scoreStr[11-i] = score % 10 + '0';
			score = score / 10;
			i++;
		}
		m_screen->setInfo(scoreStr);
		m_screen->show();
	}
finish:
	tcsetattr(STDIN_FILENO, TCSANOW, &old_tio);
}
